
package domain;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import java.util.Vector;
import domain.exceptions.CannotFlagSquareException;
import domain.exceptions.CannotPlaceAgentHereException;
import domain.exceptions.MultipleAgentsHaveTheSameLocation;
import domain.exceptions.TryingToRemoveNonExistingFlagException;

/**
 *
 * @author shaigi
 */
public class Board{

    protected String		_fileName;
    //private String 			_assigment;
    protected Square[][]	_board;
    private int				_numberOfRows;
    private int				_numberOfColumns;
    private Vector<Flag>	_flags;


    public Board(String fileName, String assignment){
        this._fileName  = fileName;
        //this._assigment	= assignment;
        this._flags     = new Vector<Flag>();
        initializeBoard();
    	parse();
    }

	public Board(Board other){
        // fileName:
    	this._fileName  = other._fileName;

    	// board:
    	this._board = new Square[other._numberOfRows][other._numberOfColumns];
    	this._flags = new Vector<Flag>();
    	for (int i = 0; i < other._numberOfRows; i++) {
    		for (int j = 0; j < other._numberOfColumns; j++) {
    			Square clonedSquare = null;
    			Square otherSquare = other.getSquare(i, j);
    			if (otherSquare instanceof SquareFree){
    				clonedSquare = new SquareFree((SquareFree)otherSquare);
    			} else if (otherSquare instanceof SquareIce){
    				clonedSquare = new SquareIce((SquareIce)otherSquare);
    			} else if (otherSquare instanceof SquareWall){
    				clonedSquare = new SquareWall((SquareWall)otherSquare);
    			}
    			this._board[i][j] = clonedSquare;

    			// flags:
    			if (this._board[i][j].isContainFlag()){
    				this._flags.addElement(this._board[i][j].getFlag());
    			}
			}
		}

    	// number of rows & columns:
    	this._numberOfRows = other._numberOfRows;
    	this._numberOfColumns = other._numberOfColumns;

    }

    public Square getSquare(int row, int column){
        return _board[row][column];
    }

    // if succeeds, sets a reference of <square> in <agent> and a reference of <agent> in <square>:
    public void placeAgent(Agent agent, Square square) throws CannotPlaceAgentHereException{
        try{
        	int col = square.getColumn();
        	int row = square.getRow();

        	// trying to place agent in the new location:
        	_board[row][col].placeAgent(agent);

        	// updating references of agent:
        	agent.setPreviousSquare(agent.getSquare());
            agent.setSquare(square);

        } catch (CannotPlaceAgentHereException e){
            throw e;
        }
    }

//    public void removeAgent(int x, int y) throws Exception{
//        try{
//            _board[x][y].removeAgent();
//        } catch (Exception e){
//            throw new Exception(e.getMessage());
//        }
//    }

    public Vector<Flag> getFlags() {
        return this._flags;
    }

    public void removeFlag(Square square) throws TryingToRemoveNonExistingFlagException {
    	// update relevant square:
    	square.removeFlag();

    	// updates flags list:
        boolean found = false;
        for (Iterator<Flag> it = _flags.iterator(); it.hasNext() && !found;) {
            Flag flag = it.next();
            if (flag.getSquare().equals(square)){
            	 _flags.remove(flag);
                found = true;
            }
        }
        if ( !found )
            throw new TryingToRemoveNonExistingFlagException();
    }

    private void initializeBoard() {
        int rowsTmp = 0;
        BufferedReader in = null;
        String str = "";
        try {
            in = new BufferedReader(new FileReader(_fileName));
        } catch (FileNotFoundException ex) {
            System.err.println("File " + _fileName + " not found");
            System.exit(4);
        }
        try {
            while ((str = in.readLine()) != null) {
                rowsTmp++;
            }
        } catch (IOException ex) {
            Debug.println(ex.getMessage());
        }
        this._numberOfRows    = rowsTmp;
        try {
            in = new BufferedReader(new FileReader(_fileName));
            str = in.readLine();
        } catch (IOException ex) {
            Debug.println(ex.getMessage());
        }
        this._numberOfColumns = str.length();
        this._board = new Square[_numberOfRows][_numberOfColumns];
    }

    protected void parse() {
        int row    = 0;
        try {
            BufferedReader in = new BufferedReader(new FileReader(_fileName));
            String str;
            while ((str = in.readLine()) != null) {
                process(str, row);
                row++;
            }
            in.close();
        } catch (IOException e) {
            Debug.println(e.getMessage());
        }
    }

    private void process(String str, int row) {

        for (int column=0; column<str.length(); column++){
            char c = str.charAt(column);
            switch (c){
                case ' ':       // empty square
                    _board[row][column] = new SquareFree(row, column);
                    break;
                case '#':       // wall
                    _board[row][column] = new SquareWall(row, column);
                    break;
                case 'F':       // flag
                    SquareFree newSquare = new SquareFree(row, column);
                    _board[row][column] = newSquare;
                    //_board[row][column].setFlag(Game.FLAG_COST);
					Flag newFlag = new Flag(newSquare, Game.FLAG_VALUE);
					try {
						_board[row][column].setFlag(newFlag);
					} catch (CannotFlagSquareException e) {
						e.printStackTrace();
					}
					_flags.addElement(newFlag);
                    break;
                case '.':       // icy patch
                    _board[row][column] = new SquareIce(row, column);
                    break;
            }
        }
    }


    public String getFileName() {
        return _fileName;
    }

    public int getNumberOfRows() {
        return _numberOfRows;
    }

    public int getNumberOfColumns() {
        return _numberOfColumns;
    }

    public boolean inSight(Square squareShooter, Square squareVictim) throws MultipleAgentsHaveTheSameLocation {
        boolean ans=true;
        int x1=squareShooter.getRow();
        int y1=squareShooter.getColumn();
        int x2=squareVictim.getRow();
        int y2=squareVictim.getColumn();
        //checking if the row or col are equal, if not returning false.
        if(x1!=x2&&y1!=y2){
            return false;
        }
        // checks if there's an obstacle in shooter's row:
        else if(x1==x2){
            for( int i=Math.min(y1, y2)+1;i<Math.max(y1,y2) && ans; i++){
                if(_board[x1][i] instanceof SquareWall  ||  _board[x1][i].isThereAgentHere()){
                    ans = false;
                }
            }
        }
        // checks if there's an obstacle in shooter's column:
        else if(y1==y2){
            for( int i=Math.min(x1, x2)+1;i<Math.max(x1,x2) && ans;i++){
                if(_board[i][y1] instanceof SquareWall  ||  _board[i][y1].isThereAgentHere()){
                    ans = false;
                }
            }
        }
        return ans;
    }//end of inSight

    /**
     *
     * @return the amount of square containing a wall
     */
    public int amountOfWallsInTheBoard(){
    	int counter=0;
    	for (int i = 0; i < _board.length; i++) {
    		for (int j = 0; j < _board[0].length; j++) {
    			if(_board[i][j] instanceof SquareWall){
    				counter++;
    			}
			}
		}
    	return counter;
    }//end of amountOfWallsInTheBoard

    @Override
    public boolean equals(Object other) {
    	if (!(other instanceof Board)){
			return false;
		}
    	Board otherBoard = (Board) other;
    	boolean ans = true;
    	for (int i = 0; i < this._numberOfRows && ans; i++) {
    		for (int j = 0; j < this._numberOfColumns && ans; j++) {
    			if (!this._board[i][j].equals(otherBoard.getSquare(i, j))){
    				ans = false;
    			}
			}
		}
    	return ans;
    }
}
